关于在 Django 中实现自定义用户模型的综合指南,增强对不同全球应用程序要求的身份验证。 学习最佳实践和高级技术。
Python Django 身份验证:掌握用于全球应用程序的自定义用户模型
Django 的内置身份验证系统是许多 Web 应用程序的强大起点。 但是,随着您的应用程序扩展并变得更加复杂,特别是对于全球受众而言,默认的 User 模型可能不足以满足需求。 这就是自定义用户模型发挥作用的地方,它提供了更大的灵活性和对用户数据和身份验证过程的控制。 本综合指南将引导您完成在 Django 中创建和实现自定义用户模型的过程,确保您的应用程序能够很好地处理不同的用户需求和安全考虑因素。
为什么要使用自定义用户模型?
默认的 Django User 模型设计有诸如用户名、密码、电子邮件、名字和姓氏等常见属性。 虽然适用于简单的应用程序,但当您需要执行以下操作时,它通常会不足:
- 存储其他用户信息:考虑一个需要存储用户偏好、各种格式的地址、首选货币或语言设置的全球电子商务平台。 这些超出了默认模型的范围。
- 更改身份验证字段:也许您想使用用户的电子邮件地址而不是用户名来验证用户身份,或者实现需要其他字段的多因素身份验证。
- 与现有数据库集成:如果您要将 Django 应用程序与具有不同用户架构的现有数据库集成,则自定义用户模型允许您将模型映射到现有数据结构。
- 增强安全性:自定义模型可以更好地控制密码哈希、密码重置机制和其他与安全相关的方面。
- 实施不同的用户角色:直接在模型中存储基于角色的访问控制 (RBAC) 数据(或引用它)比通用组和权限提供更灵活和显式的控制。
使用自定义用户模型提供了一种干净且可维护的方式来扩展用户配置文件,而无需直接修改核心 Django 身份验证系统。 对于任何预计未来增长或需要专门用户数据的项目来说,这都是最佳实践。
何时实施自定义用户模型?
实施自定义用户模型的最佳时间是在项目的开始阶段。 在生产环境中更改用户模型可能很复杂,并且可能会损坏数据。 如果您的项目已经在进行中,请在进行任何更改之前仔细考虑其影响并创建可靠的迁移计划。
以下是一般准则:
- 从自定义用户模型开始:如果您预见到需要扩展用户信息或自定义身份验证逻辑。
- 仔细考虑迁移:如果您已经有一个正在运行的 Django 项目并且有用户,并且决定切换到自定义模型。 备份您的数据库并彻底了解迁移过程。
创建自定义用户模型
在 Django 中创建自定义用户模型有两种主要方法:
- AbstractBaseUser:此方法使您可以完全控制用户模型。 您定义所有字段,包括用户名、密码、电子邮件以及您需要的任何自定义字段。
- AbstractUser:此方法继承自默认的 Django User 模型,允许您添加或覆盖现有字段。 如果您只需要添加几个额外的字段,这会更简单。
1. 使用 AbstractBaseUser(完全控制)
这是最灵活的选项,允许您从头开始定义整个用户模型。 它提供了对用户数据结构和身份验证过程的最大控制。 方法如下:
步骤 1:创建自定义用户模型
在您的 Django 应用程序(例如,“accounts”)中,创建一个“models.py”文件并定义您的自定义用户模型,该模型继承自“AbstractBaseUser”和“PermissionsMixin”:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('The Email field must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, verbose_name='email address')
first_name = models.CharField(max_length=150, blank=True)
last_name = models.CharField(max_length=150, blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
# Custom fields (Example: preferred language, timezone, etc.)
preferred_language = models.CharField(max_length=10, default='en', choices=[('en', 'English'), ('fr', 'French'), ('es', 'Spanish')])
timezone = models.CharField(max_length=50, default='UTC')
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Required when creating a superuser
objects = CustomUserManager()
def __str__(self):
return self.email
说明:
- CustomUserManager:此类是管理自定义用户模型所必需的。 它处理创建用户和超级用户。 `normalize_email` 对于确保不同区域设置和输入方法之间的电子邮件一致性非常重要。
- CustomUser:这是您的自定义用户模型。
- `email = models.EmailField(unique=True, verbose_name='email address')`:将电子邮件字段定义为用户的唯一标识符。 使用 `unique=True` 确保每个用户都有一个唯一的电子邮件地址。 详细名称改进了管理界面。
- `first_name`、`last_name`:用于存储用户名的标准字段。 `blank=True` 允许这些字段为空。
- `is_staff`、`is_active`:用于控制用户对管理面板的访问权限和帐户激活的标准字段。
- `date_joined`:记录用户帐户的创建日期。
- `preferred_language`、`timezone`:用于存储用户首选项的示例自定义字段。 `choices` 参数限制了可能的语言选项。 这对于全球应用程序至关重要。 时区对于本地化也很重要。
- `USERNAME_FIELD = 'email'`:指定电子邮件字段将用作身份验证的用户名。
- `REQUIRED_FIELDS = []`:指定使用 `createsuperuser` 命令创建超级用户时需要的字段。 在这种情况下,除了电子邮件和密码之外,不需要其他字段。
- `objects = CustomUserManager()`:将自定义用户管理器分配给模型。
- `__str__(self)`:定义用户对象如何表示为字符串(例如,在管理面板中)。
步骤 2:更新“settings.py”
通过将以下行添加到您的“settings.py”文件中,告诉 Django 使用您的自定义用户模型:
AUTH_USER_MODEL = 'accounts.CustomUser'
将“accounts”替换为您定义“CustomUser”模型的应用程序的名称。
步骤 3:创建并应用迁移
运行以下命令以创建并应用迁移:
python manage.py makemigrations
python manage.py migrate
这将为您的自定义用户模型创建一个新的数据库表。
步骤 4:使用自定义用户模型
现在,您可以在视图、模板和应用程序的其他部分中使用您的自定义用户模型。 例如,要创建一个新用户:
from accounts.models import CustomUser
user = CustomUser.objects.create_user(email='user@example.com', password='password123', first_name='John', last_name='Doe')
2. 使用 AbstractUser(添加到默认模型)
如果您只需要向默认的 Django User 模型添加几个额外的字段,则此方法更简单。 它继承了“AbstractUser”的所有现有字段和方法。 对于更简单的自定义,这可能更容易。
步骤 1:创建自定义用户模型
在您的 Django 应用程序的“models.py”文件中,定义您的自定义用户模型,该模型继承自“AbstractUser”:
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
# Add extra fields here
phone_number = models.CharField(max_length=20, blank=True, verbose_name='Phone Number')
profile_picture = models.ImageField(upload_to='profile_pictures/', blank=True)
# Custom fields (Example: preferred currency, address format, etc.)
preferred_currency = models.CharField(max_length=3, default='USD', choices=[('USD', 'US Dollar'), ('EUR', 'Euro'), ('JPY', 'Japanese Yen')])
address_format = models.CharField(max_length=50, blank=True, help_text='e.g., "Name, Street, City, Zip, Country"')
def __str__(self):
return self.username
说明:
- CustomUser:这是您的自定义用户模型,继承自“AbstractUser”。
- `phone_number`、`profile_picture`:添加到用户模型的示例字段。 `upload_to` 指定将存储个人资料图片的位置。
- `preferred_currency`、`address_format`:与全球应用程序相关的示例自定义字段。 不同的国家/地区具有截然不同的地址格式。
- `__str__(self)`:定义用户对象如何表示为字符串(例如,在管理面板中)。 这里它使用用户名。
步骤 2:更新“settings.py”
与之前一样,通过将以下行添加到您的“settings.py”文件中,告诉 Django 使用您的自定义用户模型:
AUTH_USER_MODEL = 'accounts.CustomUser'
步骤 3:创建并应用迁移
运行以下命令以创建并应用迁移:
python manage.py makemigrations
python manage.py migrate
步骤 4:使用自定义用户模型
现在,您可以在使用用户对象时访问添加的字段:
from accounts.models import CustomUser
user = CustomUser.objects.create_user(username='johndoe', password='password123', email='john.doe@example.com')
user.phone_number = '+15551234567'
user.preferred_currency = 'EUR'
user.save()
全球应用程序中自定义用户模型的最佳实践
在为面向全球受众的应用程序实施自定义用户模型时,请考虑以下最佳实践:
1. 国际化和本地化 (i18n & l10n)
存储特定于区域设置的数据:设计您的模型以适应不同的文化规范和数据格式。 以区域设置感知的方式存储日期、时间、数字和地址。
示例:
from django.utils import timezone
class CustomUser(AbstractUser):
#...
date_of_birth = models.DateField(blank=True, null=True)
def get_localized_date_of_birth(self, language_code):
if self.date_of_birth:
return timezone.localtime(timezone.make_aware(datetime.datetime.combine(self.date_of_birth, datetime.time.min))).strftime('%x') # Format according to the locale
return None
2. 时区处理
始终正确存储和处理时区。 将时区信息存储在用户模型中,并使用它以用户的本地时区显示日期和时间。
示例:
from django.utils import timezone
class CustomUser(AbstractUser):
#...
timezone = models.CharField(max_length=50, default='UTC')
def get_localized_time(self, datetime_obj):
user_timezone = pytz.timezone(self.timezone)
return timezone.localtime(datetime_obj, user_timezone)
3. 地址格式化
不同国家/地区的地址格式差异很大。 实施一个灵活的地址系统,允许用户以适合其位置的正确格式输入其地址。 考虑使用第三方库或服务来处理地址验证和格式化。
示例:
class CustomUser(AbstractUser):
#...
country = models.CharField(max_length=50, blank=True)
address_line_1 = models.CharField(max_length=255, blank=True)
address_line_2 = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=100, blank=True)
postal_code = models.CharField(max_length=20, blank=True)
def get_formatted_address(self):
# Implement logic to format address based on country
if self.country == 'US':
return f'{self.address_line_1}\n{self.address_line_2}\n{self.city}, {self.postal_code}, {self.country}'
elif self.country == 'GB':
return f'{self.address_line_1}\n{self.address_line_2}\n{self.city}\n{self.postal_code}\n{self.country}'
else:
return 'Address format not supported'
4. 货币处理
如果您的应用程序涉及金融交易,请存储用户的首选货币,并使用它来显示价格和金额。 使用像“babel”这样的库来根据用户的区域设置格式化货币值。
示例:
from babel.numbers import format_currency
class CustomUser(AbstractUser):
#...
preferred_currency = models.CharField(max_length=3, default='USD')
def get_formatted_price(self, amount):
return format_currency(amount, self.preferred_currency, locale='en_US') # Adjust locale as needed
5. 数据验证
实施强大的数据验证以确保用户输入有效且一致。 使用 Django 的内置验证器或创建自定义验证器以强制执行数据完整性。
示例:
from django.core.validators import RegexValidator
class CustomUser(AbstractUser):
#...
phone_number = models.CharField(
max_length=20,
blank=True,
validators=[
RegexValidator(
regex=r'^\+?\d{9,15}$',
message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
),
]
)
6. 安全注意事项
密码哈希:默认情况下,Django 的身份验证系统使用强大的密码哈希算法。 确保您使用的是最新版本的 Django,以便从最新的安全更新中受益。
双重身份验证 (2FA):实施 2FA 以向用户帐户添加额外的安全层。 有各种适用于此的 Django 包,例如“django-otp”。 这在处理敏感用户数据或金融交易时尤其重要。
数据保护:遵循数据保护和隐私的最佳实践,尤其是在处理敏感用户信息时。 遵守相关的数据保护法规,例如 GDPR 和 CCPA。 考虑数据加密、匿名化和令牌化技术。
7. 测试
编写全面的单元测试和集成测试,以确保您的自定义用户模型按预期工作,并且您的身份验证系统是安全的。 测试不同的场景,包括有效和无效的用户输入、密码重置工作流以及权限检查。
8. 文档
彻底记录您的自定义用户模型和身份验证系统。 这将使其他开发人员更容易理解和维护您的代码。 包括有关每个字段的用途、身份验证流程以及任何安全注意事项的信息。
高级技术和注意事项
1. 自定义用户管理器
如“AbstractBaseUser”示例所示,自定义用户管理器对于创建和管理用户至关重要。 它们允许您定义用于创建用户的自定义逻辑,例如为某些字段设置默认值或执行其他验证。
2. 代理模型
代理模型允许您向用户模型添加方法,而无需更改数据库架构。 这对于添加特定于您的应用程序的自定义逻辑或计算可能很有用。
3. 使用配置文件模型扩展用户模型
您可以创建一个单独的配置文件模型,该模型与用户模型具有一对一的关系,而不是直接向用户模型添加许多字段。 这有助于保持用户模型的干净和有条理。
from django.db import models
from django.conf import settings
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
# Additional fields
bio = models.TextField(blank=True)
location = models.CharField(max_length=100, blank=True)
请记住创建一个信号,以便在创建用户时自动创建 UserProfile:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings
from .models import UserProfile
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
4. 单点登录 (SSO)
对于需要与其他服务集成的大型组织或应用程序,请考虑使用 OAuth 2.0 或 SAML 等协议实施单点登录 (SSO)。 Django 提供了几个简化 SSO 集成的包,例如“django-allauth”。
5. 审核日志记录
实施审核日志记录以跟踪用户活动和对用户数据的更改。 这对于安全监控、合规性和调试可能很有用。 像“django-auditlog”这样的包可以帮助自动化此过程。
结论
在 Django 中创建和实施自定义用户模型提供了构建强大且可扩展的身份验证系统所需的灵活性和控制,尤其是在全球应用程序中。 通过遵循本指南中概述的最佳实践,您可以确保您的应用程序能够很好地处理不同的用户需求,维护数据完整性,并为世界各地的用户提供安全且用户友好的体验。 请记住仔细计划您的实施,考虑用户的需求,并在流程的每个阶段优先考虑安全性。 在“AbstractBaseUser”和“AbstractUser”之间进行选择取决于所需的自定义级别。 对于重大更改,“AbstractBaseUser”提供了更多的控制。 对于简单的扩展,“AbstractUser”提供了更平滑的过渡。 彻底的测试对于确保自定义用户模型与 Django 应用程序的其余部分无缝集成并满足所有安全要求至关重要。 采纳国际化、本地化和时区处理的最佳实践,以提供真正全球化的体验。 这将极大地促进您的应用程序在全球不同市场中的成功和采用。